#!/usr/bin/perl
use warnings;
use strict;

use feature qw/say/;
use Carp qw(confess cluck);
use Cwd qw{abs_path    cwd};
use Data::Dumper::Concise;
use Env;
use File::Basename;
use File::Path qw (make_path);
use File::Slurp;
use FileHandle;
use FindBin qw($RealBin  $RealScript);
use Getopt::Long;
use YAML;




use lib $RealBin;

use CON;                                                                                                 
use Carp::Always::Color;                                                                                 
use Data::Printer            {indent        =>                   1};                                     
#use Math::Interpolate        qw(derivatives constant_interpolate linear_interpolate robust_interpolate); 
#use Statistics::Descriptive;                                                                             
use ZipSlurp;                                                                                            
use FlHandle;


my $opt_force = undef;
$|=1;
#


my $file_lib = q//;
my $out_dir;
my $file_pin ;

GetOptions(
    'force'      => \$opt_force,
    'lib=s'      => \$file_lib,
    'out_dir=s'  => \$out_dir,
    'pinfile=s'  => \$file_pin,
);

if(not $file_lib ){ 
    printf "%s \n", &CONm( "-E- no lib file provided ", 5);
    print_usage();
}

if (not -e $file_lib) { 
    printf "%s \n", &CONm( "-E- lib file $file_lib not exists ", 5);
    print_usage();
}

my $abs_file_pin;
my ($pinFileNameBase,$pinDir,$pinSuffix);
my $rHpin={};
my @AHpinN;
if ($file_pin) {
    if ( not -e $file_pin ) {
        printf "%s \n", &CONm( "-E- pin list file $file_pin provided but file not exists ", 5 );
        &print_usage();
        exit;
    }
    $abs_file_pin = abs_path($file_pin);
    ( $pinFileNameBase, $pinDir, $pinSuffix ) = fileparse( $abs_file_pin, qr/\.?[^.]*(\.gz)?/ );
    printf "-i-reading pinlist file:%8s\n", &CONm(" $abs_file_pin");
    my $PinString = q//;
    &readZF( qq{$abs_file_pin}, \$PinString );
    @AHpinN = map { lc $_ } ( $PinString =~ m{ [^ \t\n\(\)\{\}"]+ }sixg );
    foreach my $pi3 (@AHpinN) {
        my ( $PinN, $RelatedPin, $TimingType, $Value ) = split /,/, $pi3;
        my $ttype2 = lc $TimingType;
        $rHpin->{$PinN}{$RelatedPin}{$ttype2} = $Value;
    }
} else {
    printf "-i- no pin file provided %8s\n", "";
    exit;
}

my $abs_file_lib = abs_path($file_lib);
my $str_head = q//;
&FlHandle::ReadPartOfZFile( $abs_file_lib, \$str_head, 10_480);

printf "-i-input lib file:%8s\n", &CONm(" $abs_file_lib");

# pre-defined regexp patterns 
#{{
my $ws=qr([ \t]*)six;
my $parenth_name        = qr { $ws \($ws \S+ $ws \) $ws}six;
my $parenth_only        = qr { \s* \(\s*  \)\s*}six;
my $braces              = qr{ ( \s* \{ (?: (?> [^\{\}]+ ) | (?-1) )* \}) }six;
my $parentheses         = qr{ ( \s* \( (?: (?> [^\(\)]+ ) | (?-1) )* \)) }six;
my $FloatPNumber2       = qr { -? \b \d+\.? \d* \b}six;
my $RisePat             = qr{\b (?:rise_constraint|rise_transition|cell_rise )}six;
my $FallPat             = qr{\b (?:fall_constraint|fall_transition|cell_fall )}six;
my $RisePatterns        = qr{$RisePat $parenth_name $braces}six;
my $FallPatterns        = qr{$FallPat $parenth_name $braces}six;
my $RiseFallPatterns    = qr{(?:$RisePat|$FallPat )$parenth_name $braces}six;
my $ValuesPatterns      = qr{values $parentheses}six;
my $TimingTypePattern   = qr{\b timing_type $ws : $ws (\S+)}six;
my $minDelayFlagPattern = qr{\b min_delay_flag $ws : $ws "? true "? }six;
#_}}_

my  $str_signature = &FlHandle::GetYWRndStr;
my $strT7925694098 = <<"EOT7925694098";
timing () {
    related_pin : id69589 ;
    timing_type : id66136 ;
id7787769274 
} /* $str_signature injected */
EOT7925694098
#    rise_constraint (scalar) { values("54.4355255"); }
#    fall_constraint (scalar) { values("54.1992180"); }

my ($LibFileNameBase,$libDir,$suffix) = fileparse($abs_file_lib, qr/\.[^.]*(\.(?:bz2|gz))?/);
my $pin_name ;
my $str_li =q//;
&readZF($abs_file_lib, \$str_li);

$str_li =~s/(pin $parenth_name $braces)/&TreatPinBlock($1)/sixge;

my $out_file_full_name;
if ($out_dir) {
    if ( not -d $out_dir ) { make_path($out_dir) or Carp::confess "cannot create $out_dir"; }
} else {
    $out_dir = $libDir;
}
$out_dir = abs_path($out_dir);

my $rng = &GetRndHex;
$out_file_full_name = "$out_dir/$LibFileNameBase.preinj.$rng$suffix";
printf "-i-backing up to file %8s\n", &CONm( " $out_file_full_name ", 2 );

system "cp -f $abs_file_lib $out_file_full_name" and Carp::confess "cannot backup orig file $file_lib into $out_file_full_name";

$out_file_full_name = "$out_dir/$LibFileNameBase$suffix";

my $time_string_whole = localtime();
my $str_stamp         = "/* injected at $time_string_whole.  \n using $RealBin/$RealScript \n\n";

my $PinListString = &YAML::Dump( $rHpin );
$str_stamp .= "\n injected timing assertions of pins (in YAML format) : \n  $PinListString \n";

$str_stamp .= " */\n";
$str_li = $str_stamp . $str_li;
&writeZF( \$str_li, $out_file_full_name );


#########
#  sub  #
#########

sub TreatPinBlock 
{#_{{_
    my ($pStr) = @_;
    if ( not $pStr ) { return; }

    my ($pname) = ( $pStr =~ m/pin ($parenth_name)/six );
    $pname =~ s#[" \t\(\)]##g;
    $pname = lc $pname;
    my $found_pin = 0;

    if ( $rHpin->{$pname} ) {
        my $rH637316253   = $rHpin->{$pname};
        my $str5785416097 = q//;
        foreach my $k1 ( sort keys %{$rH637316253} ) {
            my $rH103495019 = $rHpin->{$pname}{$k1};
            foreach my $k2 ( sort keys %$rH103495019 ) {
                my $str0 = $strT7925694098; # copying the template of timing arc 
                $str0 =~ s#\b id69589 \b#"$k1"#six;
                $str0 =~ s#\b id66136 \b#"$k2"#six;
                my @Aarc = ();
                if ( $k2 =~ m# (?:setup|hold|removal|recover) #six ) { # if not constraints, assuming 
                    @Aarc = qw(rise_constraint fall_constraint);       # like output pin's cell-rise cell-fall
                } else {                                               # 
                    @Aarc = qw(cell_rise cell_fall );                  # 
                }                                                      # 
                my $st4778056640 = q//;
                foreach my $ia1 (@Aarc) {
                    $st4778056640 .= sprintf "    %s (scalar) {values(\"%s\");}\n", $ia1, $rH103495019->{$k2};
                }

                $str0 =~ s#\b id7787769274 \b#$st4778056640#six; # injecting timing arcs
                $str5785416097 .= $str0;                         # 
            }
        } # {   {
        $pStr =~ s/} ([^\{\}]*)$ /\n$str5785416097}$1/six;
    } else {
    }
    return $pStr;
}#_}}_


sub print_usage
{#_{{_

  my $string0 = &CONm( "Required:", 5);
  my $string0p1 = &CONm( "$RealBin/$RealScript", 2);

#my $string1  = &CONm( "PinName,RelatedPin,TimingType,AddingValue", );
#my $string2  = &CONm( "d,clk1,setup_rising,76\nd,clk1,hold_rising,60" );
my $string1 = sprintf "%s,%s,%s,%s\n", &CONm( "PinName", 3 ), &CONm( "RelatedPin", 4 ), &CONm( "TimingType",   5 ), &CONm( "AddingValue", 6 );
my $string2 = sprintf "%s,%s,%s,%s\n", &CONm( "d",       3 ), &CONm( "clk1",       4 ), &CONm( "setup_rising", 5 ), &CONm( "76",          6 );
my $string2b = sprintf "%s,%s,%s,%s\n", &CONm( "d",       3 ), &CONm( "clk1",       4 ), &CONm( "hold_rising", 5 ), &CONm( "60",          6 );

my $st6487855411 = <<'EO6487855411';
timing () {
    related_pin : "clk" ;
    timing_type : "setup_rising" ;
    rise_constraint (scalar) {values("76");}
    fall_constraint (scalar) {values("76");}
 
} /* 20ww39d219_777d3e9b injected */
EO6487855411

my $st4231125279 = &CONm($st6487855411, 10);

  my $ScaleHelp = <<"__EOHELP__";


#######################################################################
#                                USAGE                                #
#######################################################################


 $string0p1 -lib <lib_name> -pin <pinlist_file> -out <outdir> 

# where

#######################
#  -lib   <lib_name>  #
#######################
      
    
# $string0 : Full path of lib file to be adjusted

#  -FYI- original file will be saved as file.preinj.random.lib 


#########################
#  -pin <pinlist_file>  #
#########################

# $string0 Full path to a file containing pin names; only
# those pins mentioned in this pin-file would be adjusted.
#
# Syntax/format is like

$string1

# e.g., below in to inject a pin-d's timing arc
# of setup_rising timing_type, related to clk1
# (related_pin), and the timing constraint value is 76ps.
#

$string2

# and below is to inject pin-d's hold_rising
# (related to clk1) constraint of 60ps. 

$string2b

# a typical example of injected timing ar will look like

$st4231125279

###################
#  -out <outdir>  #
###################

# Optional: output directory; default is libfile's residing
# directory
#


__EOHELP__

 say $ScaleHelp;

  exit;

}#_}}_


